package com.onlyMe.net.library
{
	import com.flesler.componentas.core.Destroyable;
	
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;

	public final class Library extends EventDispatcher implements Destroyable
	{
		public static function fromLoaderInfo(loaderInfo:LoaderInfo):Library
		{
			var lib:Library = new Library();
			lib.loaderInfo = loaderInfo;
			return lib;
		}
		
		private var loader:Loader;
		private var loaderInfo:LoaderInfo;
		private var _isDestroyed:Boolean = false;
		private var _loaded:Boolean = false;
		private var failed:Boolean = false;
		private var url:String;
		
		public function Library(shared:Boolean=false, url:String=null)
		{
			if (url)
				load(shared, url);
		}
		
		private function load(shared:Boolean, url:String):void
		{
			this.url = url;
			
			loader = new Loader();
			loaderInfo = loader.contentLoaderInfo;
			loaderInfo.addEventListener(Event.COMPLETE, whenLibraryIsLoaded);
			loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, whenLibraryFailsToLoad);
			loaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, whenLibraryFailsToLoad);

			var domain:ApplicationDomain = new ApplicationDomain(
				shared ? ApplicationDomain.currentDomain:null
			);
			
			loader.load(
				new URLRequest(url),
				new LoaderContext(false, domain)
			);
		}
		
		private function warning(...args:Array):void
		{
			// TEMP
			args.unshift('Library >');
			throw new Error(args.join(' '));
		}
		
		// Loading
		
		public function get loaded():Boolean
		{
			return _loaded || failed;
		}
		
		public function get loadFailed():Boolean
		{
			return failed;
		}
		
		private function get domain():ApplicationDomain
		{
			return loaderInfo.applicationDomain;
		}
		
		public function get content():DisplayObject
		{
			return loader ? loader.content:null;
		}
		
		private function whenLibraryIsLoaded(e:Event):void
		{
			_loaded = true;
			if (loaderInfo)
				event(LibraryEvent.LOADED);
		}
		
		private function whenLibraryFailsToLoad(e:Event):void
		{
			warning('Failed to load the swf:', url);
			failed = true;
			event(LibraryEvent.LOAD_FAILED);
		}
		
		// Item retrieving
		
		public function hasClass(name:String):Boolean
		{
			return domain !== null && domain.hasDefinition(name);
		}
		
		public function getClass(name:String):Class
		{
			if (!loaded)
				warning("The swf "+url+" isn't loaded yet");

			if (!failed && !hasClass(name))
			{
				warning('No class named "'+name+'" was found on '+url);
				return null;
			}

			return domain.getDefinition(name) as Class;
		}
		
		public function getItem(className:String):Object
		{
			var Klass:Class = getClass(className);
			return Klass ? new Klass():null;
		}
		
		private function event(type:String):void
		{
			dispatchEvent(new LibraryEvent(type));
		}
		
		public function get isDestroyed():Boolean
		{
			return _isDestroyed;
		}
		
		public function destroy():void
		{
			// Can throw unexpected IO Errors
			try {
				if (loader)
				{
					if (_loaded)
						loader.unload();
					else if (!failed)
						loader.close();
				}
			}catch(err:Error){}
			
			loaderInfo.removeEventListener(Event.COMPLETE, whenLibraryIsLoaded);
			loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, whenLibraryFailsToLoad);
			loaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, whenLibraryFailsToLoad);
			loaderInfo = null;
			loader = null;
			_isDestroyed = true;
		}
	}
}
